package com.example.ass7;

import java.util.ArrayList;
import java.util.List;

import com.example.ass7.collision.Collidable;
import com.example.ass7.collision.CollisionInfo;
import com.example.ass7.geometryprimitives.Line;
import com.example.ass7.geometryprimitives.Point;

/**
 * @author Meirav Ben Izhak
 */
public class GameEnvironment {

    // members
    private final List<Collidable> gamesCollidables;
    private int collidablesAmount;

    /**
     * constructor.
     */
    public GameEnvironment() {
        this.gamesCollidables = new ArrayList<Collidable>();
        this.collidablesAmount = 0;

    }

    // private Rectangle gameBoardLimits;

    // add the given collidable to the environment.
    /**
     * @param c
     *            c
     */
    public void addCollidable(Collidable c) {
        this.gamesCollidables.add(c);
        this.collidablesAmount += 1;
    }

    /**
     * @return List<Collidable>
     */
    public List<Collidable> getGamesCollidables() {
        return this.gamesCollidables;
    }

    /**
     * void.
     */
    public void clearCollidableList() {
        this.gamesCollidables.clear();
        this.collidablesAmount = 0;
    }

    /**
     * maybe this method should only exist in Game... public void
     * removeCollidable(Collidable c) { this.gamesCollidables.remove(c);
     * this.collidablesAmount -= 1; }
     */

    // Assume an object moving from line.start() to line.end().
    // If this object will not collide with any of the collidables
    // in this collection, return null. Else, return the information
    // about the closest collision that is going to occur.
    /**
     * @param trajectory
     *            trajectory
     * @return CollisionInfo
     */
    public CollisionInfo getClosestCollision(Line trajectory) {
        List<CollisionInfo> nextHit = new ArrayList<CollisionInfo>();
        nextHit = this.intersectionsWithTrajectory(trajectory);
        CollisionInfo toReturn = this.findClosestCollidableToStart(trajectory,
                nextHit);
        return toReturn;
    }

    /**
     * @param trajectory
     *            trajectory
     * @return List<CollisionInfo>
     */
    public List<CollisionInfo> intersectionsWithTrajectory(Line trajectory) {
        List<CollisionInfo> nextHit = new ArrayList<CollisionInfo>();
        for (int iter = 0; iter < this.collidablesAmount; iter++) {
            Point collide = trajectory
                    .closestIntersectionToStartOfLine(this.gamesCollidables
                            .get(iter).getCollisionRectangle());
            if (collide != null) {
                CollisionInfo toAdd = new CollisionInfo(collide,
                        this.gamesCollidables.get(iter));
                nextHit.add(toAdd);
            }
        }
        return nextHit;
    }

    /**
     * @param line
     *            line
     * @param toPass
     *            toPass
     * @return CollisionInfo
     */
    public CollisionInfo findClosestCollidableToStart(Line line,
            List<CollisionInfo> toPass) {
        CollisionInfo toReturn;
        if (toPass.size() == 0) {
            toReturn = new CollisionInfo(null, null);
        } else if (toPass.size() == 1) {
            toReturn = toPass.get(0);
        } else {
            while (toPass.size() > 1) {
                for (int iter = toPass.size() - 1; iter > 0; iter--) {
                    double distanceFromStart1 = toPass.get(iter)
                            .getCollisionPoint().distance(line.start());
                    double distanceFromStart2 = toPass.get(iter - 1)
                            .getCollisionPoint().distance(line.start());

                    if (distanceFromStart1 < distanceFromStart2) {
                        CollisionInfo toRemove = toPass.get(iter - 1);
                        toPass.remove(toRemove);
                    } else {
                        CollisionInfo toRemove = toPass.get(iter);
                        toPass.remove(toRemove);
                    }
                }

                // for (int iter = 0; iter < toPass.size(); iter++) {
                // double distanceFromStart1 = toPass.get(iter)
                // .getCollisionPoint().distance(line.start());
                // double distanceFromStart2 = toPass.get(iter + 1)
                // .getCollisionPoint().distance(line.start());
                //
                // if (distanceFromStart1 < distanceFromStart2) {
                // CollisionInfo toRemove = toPass.get(iter + 1);
                // toPass.remove(toRemove);
                // } else {
                // CollisionInfo toRemove = toPass.get(iter);
                // toPass.remove(toRemove);
                // }
                // }
            }
            toReturn = toPass.get(0);
        }
        return toReturn;
    }

    /**
     * @param trajectory
     *            trajectory
     * @return boolean
     */
    public boolean willThereBeACollision(Line trajectory) {
        return this.getClosestCollision(trajectory).getCollisionPoint() != null;
    }

    /**
     * @return int
     */
    public int getCollidablesAmount() {
        return this.collidablesAmount;
    }

    /**
     * @param pCollidablesAmount
     *            pCollidablesAmount
     */
    public void setCollidablesAmount(int pCollidablesAmount) {
        this.collidablesAmount = pCollidablesAmount;
    }

}